package cn.shengchao.examstar.knowledge.application.query.impl;

import cn.shengchao.examstar.knowledge.application.dto.KnowledgeTreeNodeDTO;
import cn.shengchao.examstar.knowledge.application.query.KnowledgeTreeQuery;
import cn.shengchao.examstar.knowledge.domain.entity.KnowledgePoint;
import cn.shengchao.examstar.knowledge.domain.entity.KnowledgePointMastery;
import cn.shengchao.examstar.knowledge.domain.enums.MasteryLevel;
import cn.shengchao.examstar.knowledge.domain.repository.KnowledgePointMasteryRepository;
import cn.shengchao.examstar.knowledge.domain.repository.KnowledgePointRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 知识树查询实现类
 */
@Service
public class KnowledgeTreeQueryImpl implements KnowledgeTreeQuery {

    @Autowired
    private KnowledgePointRepository knowledgePointRepository;

    @Autowired
    private KnowledgePointMasteryRepository knowledgePointMasteryRepository;

    @Override
    public List<KnowledgeTreeNodeDTO> getFullKnowledgeTree(Long userId, Long specificationTypeId) {
        // 1. 获取指定类目的所有知识点
        List<KnowledgePoint> knowledgePoints = knowledgePointRepository.findBySpecificationTypeId(specificationTypeId);
        
        // 2. 获取用户的所有知识点掌握度
        List<KnowledgePointMastery> masteries = knowledgePointMasteryRepository.findByUserId(userId);
        Map<Long, KnowledgePointMastery> masteryMap = masteries.stream()
                .collect(Collectors.toMap(KnowledgePointMastery::getKnowledgePointId, mastery -> mastery));
        
        // 3. 构建并计算树结构
        return buildAndCalculateKnowledgeTree(knowledgePoints, masteryMap, 0L);
    }

    @Override
    public List<KnowledgeTreeNodeDTO> getRootAndLeafKnowledgeTree(Long userId, Long specificationTypeId) {
        // 1. 首先构建完整的知识树，其中包含了所有父节点的计算结果
        List<KnowledgeTreeNodeDTO> fullTree = getFullKnowledgeTree(userId, specificationTypeId);

        // 2. 然后从完整树中筛选出根节点和它们的叶子节点
        return filterRootAndLeafNodes(fullTree);
    }

    /**
     * 从完整树中筛选出根节点及其所有叶子节点
     *
     * @param fullTree 完整的知识树
     * @return 仅包含根节点和叶子节点的树
     */
    private List<KnowledgeTreeNodeDTO> filterRootAndLeafNodes(List<KnowledgeTreeNodeDTO> fullTree) {
        if (fullTree == null || fullTree.isEmpty()) {
            return Collections.emptyList();
        }
        List<KnowledgeTreeNodeDTO> result = new ArrayList<>();
        for (KnowledgeTreeNodeDTO rootNode : fullTree) {
            // 复制根节点信息，但不包括其子节点
            KnowledgeTreeNodeDTO newRoot = KnowledgeTreeNodeDTO.builder()
                    .id(rootNode.getId())
                    .name(rootNode.getName())
                    .description(rootNode.getDescription())
                    .parentId(rootNode.getParentId())
                    .sort(rootNode.getSort())
                    .path(rootNode.getPath())
                    .isLeaf(false) // 根节点永远不是叶子节点
                    .degreeOfMastery(rootNode.getDegreeOfMastery()) // 使用已计算好的掌握度
                    .accuracyRate(rootNode.getAccuracyRate()) // 使用已计算好的正确率
                    .build();

            // 查找并添加所有后代叶子节点
            List<KnowledgeTreeNodeDTO> leafNodes = new ArrayList<>();
            findAllLeafNodes(rootNode, leafNodes);
            newRoot.setChildren(leafNodes);
            result.add(newRoot);
        }
        return result;
    }

    /**
     * 递归查找并收集所有叶子节点
     * @param node 当前节点
     * @param leafNodes 收集叶子节点的列表
     */
    private void findAllLeafNodes(KnowledgeTreeNodeDTO node, List<KnowledgeTreeNodeDTO> leafNodes) {
        if (node.isLeaf()) {
            leafNodes.add(node);
            return;
        }
        if (node.getChildren() != null) {
            for (KnowledgeTreeNodeDTO child : node.getChildren()) {
                findAllLeafNodes(child, leafNodes);
            }
        }
    }

    /**
     * 采用后序遍历（自底向上）构建知识树，并计算父节点的掌握度与正确率
     *
     * @param knowledgePoints 知识点列表
     * @param masteryMap      掌握度映射
     * @param parentId        父节点ID
     * @return 知识树节点列表
     */
    private List<KnowledgeTreeNodeDTO> buildAndCalculateKnowledgeTree(
            List<KnowledgePoint> knowledgePoints,
            Map<Long, KnowledgePointMastery> masteryMap,
            Long parentId) {

        // 筛选当前层级的节点
        List<KnowledgePoint> currentLevelPoints = knowledgePoints.stream()
                .filter(point -> Objects.equals(point.getParentId(), parentId))
                .sorted(Comparator.nullsLast(Comparator.comparing(KnowledgePoint::getSort, Comparator.nullsLast(Long::compareTo))))
                .collect(Collectors.toList());

        if (currentLevelPoints.isEmpty()) {
            return Collections.emptyList();
        }

        List<KnowledgeTreeNodeDTO> result = new ArrayList<>();

        for (KnowledgePoint point : currentLevelPoints) {
            // 递归构建子树
            List<KnowledgeTreeNodeDTO> children = buildAndCalculateKnowledgeTree(
                    knowledgePoints, masteryMap, point.getId());

            boolean isLeaf = children.isEmpty();

            KnowledgeTreeNodeDTO node = KnowledgeTreeNodeDTO.builder()
                    .id(point.getId())
                    .name(point.getName())
                    .description(point.getDescription())
                    .parentId(point.getParentId())
                    .sort(point.getSort())
                    .path(point.getPath())
                    .isLeaf(isLeaf)
                    .children(children)
                    .build();

            if (isLeaf) {
                // 如果是叶子节点，直接从 masterMap 获取掌握度信息
                KnowledgePointMastery mastery = masteryMap.get(point.getId());
                if (mastery != null) {
                    node.setDegreeOfMastery(mastery.getDegreeOfMastery());
                    node.setAccuracyRate(mastery.getAccuracyRate());
                } else {
                    node.setDegreeOfMastery(MasteryLevel.UNTESTED.getCode());
                    node.setAccuracyRate("0");
                }
            } else {
                // 如果是父节点，从其所有后代叶子节点计算综合掌握度和正确率
                List<KnowledgeTreeNodeDTO> descendantLeaves = new ArrayList<>();
                findAllLeafNodes(node, descendantLeaves);
                Map<String, Object> stats = calculateParentNodeStats(descendantLeaves);
                node.setDegreeOfMastery((String) stats.get("degreeOfMastery"));
                node.setAccuracyRate((String) stats.get("accuracyRate"));
            }

            result.add(node);
        }

        return result;
    }
    
    /**
     * 根据叶子节点列表计算父节点的综合掌握度和平均正确率
     *
     * @param leafNodes 后代叶子节点列表
     * @return 包含 "degreeOfMastery" 和 "accuracyRate" 的 Map
     */
    private Map<String, Object> calculateParentNodeStats(List<KnowledgeTreeNodeDTO> leafNodes) {
        Map<String, Object> stats = new HashMap<>();

        // 过滤掉掌握程度为 "N" (UNTESTED) 的叶子节点，后续所有计算都基于这个过滤后的列表
        List<KnowledgeTreeNodeDTO> testedLeafNodes = leafNodes.stream()
                .filter(leaf -> !MasteryLevel.UNTESTED.getCode().equals(leaf.getDegreeOfMastery()))
                .toList();

        if (testedLeafNodes.isEmpty()) {
            stats.put("degreeOfMastery", MasteryLevel.UNTESTED.getCode());
            stats.put("accuracyRate", "0");
            return stats;
        }

        // 1. 基于过滤后的节点计算平均正确率
        BigDecimal totalAccuracy = BigDecimal.ZERO;
        int validAccuracyCount = 0;
        for (KnowledgeTreeNodeDTO leaf : testedLeafNodes) {
            try {
                totalAccuracy = totalAccuracy.add(new BigDecimal(leaf.getAccuracyRate()));
                validAccuracyCount++;
            } catch (Exception e) {
                // 忽略无法解析的正确率
            }
        }
        BigDecimal averageAccuracy = (validAccuracyCount > 0)
                ? totalAccuracy.divide(new BigDecimal(validAccuracyCount), 2, RoundingMode.HALF_UP)
                : BigDecimal.ZERO;
        stats.put("accuracyRate", averageAccuracy.toPlainString());

        // 2. 基于过滤后的节点计算父节点综合掌握度（加权平均分）
        Map<String, Integer> scoreMap = new HashMap<>();
        scoreMap.put(MasteryLevel.EXPERT.getCode(), 4);
        scoreMap.put(MasteryLevel.PROFICIENT.getCode(), 3);
        scoreMap.put(MasteryLevel.UNDERSTOOD.getCode(), 2);
        scoreMap.put(MasteryLevel.BASIC.getCode(), 1);

        double totalScore = 0;
        for (KnowledgeTreeNodeDTO leaf : testedLeafNodes) {
            totalScore += scoreMap.getOrDefault(leaf.getDegreeOfMastery(), 0);
        }

        String finalMastery;
        double averageScore = totalScore / testedLeafNodes.size();
        if (averageScore >= 3.5) {
            finalMastery = MasteryLevel.EXPERT.getCode();
        } else if (averageScore >= 2.5) {
            finalMastery = MasteryLevel.PROFICIENT.getCode();
        } else if (averageScore >= 1.5) {
            finalMastery = MasteryLevel.UNDERSTOOD.getCode();
        } else {
            finalMastery = MasteryLevel.BASIC.getCode();
        }
        stats.put("degreeOfMastery", finalMastery);

        return stats;
    }
} 